
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: FieldModel.java 310 2010-02-05 21:20:06Z archie.cobbs $
 */

package org.dellroad.sidekar.schema.model;

import org.dellroad.sidekar.Sidekar;
import org.dellroad.sidekar.schema.SchemaMismatchException;

/**
 * Superclass for field types (i.e., types that are valid as an entity field).
 */
public abstract class FieldModel extends AbstractModel {

    protected final String typeName;

    // Normal constructor
    FieldModel(String typeName) {
        this.typeName = typeName;
    }

    // Copy constructor
    FieldModel(FieldModel original) {
        super(original);
        this.typeName = original.typeName;
    }

    /**
     * Validate this field. In particular, any {@link EntityModel}s that are referenced
     * by this field must appear in the schema as well.
     *
     * <p>
     * This method should not recurse on any referenced entities.
     *
     * <p>
     * This implementation validates the field name only. Subclasses should override and invoke
     * this method and then do any additional validation (as necessary).
     *
     * @param schema the schema being validated
     * @throws IllegalArgumentException if this type is invalid
     */
    public void validate(SchemaModel schema) {
        Sidekar.checkValidFieldName(getName());
    }

    /**
     * Determine if the given field has the same in-database structure
     * as this field. In other words, a schema update should be required
     * if and only if this returns false.
     *
     * <p>
     * This implementation just checks that the fields have the same type name as passed to the constructor.
     * Subclasses should override this method, invoking this superclass method first, then
     * further verify type-specific details.
     *
     * @throws SchemaMismatchException if the fields are different
     */
    public void checkSameField(FieldModel that) {
        if (!that.typeName.equals(this.typeName)) {
            throw new SchemaMismatchException("field type mismatch: "
              + this.getTypeDescription() + " vs. " + that.getTypeDescription());
        }
    }

    /**
     * Visit the given switch based according to this field's type.
     */
    public abstract <R> R visit(FieldModelSwitch<R> sw);

    /**
     * Get a textual description of this field's type.
     */
    public abstract String getTypeDescription();

    @Override
    public String toString() {
        String name = getName();
        return "field " + (name != null ? "`" + name + "'" : "<anonymous>") + " of type " + this.getTypeDescription();
    }
}

